package com.iflytek.jcy.supervise.job.component.web.filter;

import javax.servlet.FilterChain;
import javax.servlet.ReadListener;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletOutputStream;
import javax.servlet.WriteListener;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;

import com.fasterxml.jackson.core.JsonProcessingException;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.filter.OncePerRequestFilter;

/**
 * @author <a href=mailto:ktyi@iflytek.com>伊开堂</a>
 * @date 2022/6/15
 */
public class ResponseReplaceFilter extends OncePerRequestFilter {
    protected final Logger logger = LoggerFactory.getLogger(ResponseReplaceFilter.class);

    private String replaceOld;
    private String replaceNew;
    public ResponseReplaceFilter(String replaceOld, String replaceNew) {
        this.replaceOld = replaceOld;
        this.replaceNew = replaceNew;
    }


    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
            FilterChain filterChain) throws ServletException, IOException {
        logger.info("path:{}", request.getRequestURI());

        ModifyRequestBodyWrapper modifyRequestBodyWrapper = new ModifyRequestBodyWrapper(request);
        String oldRequestBody = modifyRequestBodyWrapper.getOldRequestBody();
        String newRequestBody = changeRequestBody(oldRequestBody);
        modifyRequestBodyWrapper.setNewRequestBody(newRequestBody);

        ModifyResponseBodyWrapper modifyResponseBodyWrapper = new ModifyResponseBodyWrapper(response);
        filterChain.doFilter(modifyRequestBodyWrapper, modifyResponseBodyWrapper);
        String oldResponseBody = modifyResponseBodyWrapper.getResponseBody();
        logger.info("oldResponseBody:{}", oldResponseBody);
        String newResponseBody = changeResponseBody(oldResponseBody);
        logger.info("newResponseBody:{}", newResponseBody);

        byte[] responseBodyData = newResponseBody.getBytes(StandardCharsets.UTF_8);
        ServletOutputStream out = response.getOutputStream();
        out.write(responseBodyData);
    }

    /**
     * 修改请求体
     *
     * @param oldRequestBody 修改前的请求体
     *
     * @return 修改后的请求体
     */
    public String changeRequestBody(String oldRequestBody) throws JsonProcessingException {
        return oldRequestBody;
    }

    /**
     * 修改响应体
     *
     * @param oldResponseBody 修改前的响应体
     *
     * @return 修改够的响应体
     */
    public String changeResponseBody(String oldResponseBody) {
        if (StringUtils.isBlank(oldResponseBody)) {
            return oldResponseBody;
        }
        return StringUtils.replace(oldResponseBody, replaceOld, replaceNew);
    }


    public class ModifyRequestBodyWrapper extends HttpServletRequestWrapper {

        private String oldRequestBody;

        private String newRequestBody;

        public ModifyRequestBodyWrapper(HttpServletRequest request) throws IOException {
            super(request);
            this.oldRequestBody = IOUtils.toString(request.getInputStream(), request.getCharacterEncoding());
        }

        @Override
        public ServletInputStream getInputStream() throws IOException {
            return new ServletInputStream() {
                final ByteArrayInputStream bis = new ByteArrayInputStream(newRequestBody.getBytes(StandardCharsets.UTF_8));

                @Override
                public boolean isFinished() {
                    return true;
                }

                @Override
                public boolean isReady() {
                    return true;
                }

                @Override
                public void setReadListener(ReadListener readListener) {

                }

                @Override
                public int read() throws IOException {
                    return bis.read();
                }
            };
        }

        public String getOldRequestBody() {
            return oldRequestBody;
        }

        public void setOldRequestBody(String oldRequestBody) {
            this.oldRequestBody = oldRequestBody;
        }

        public String getNewRequestBody() {
            return newRequestBody;
        }

        public void setNewRequestBody(String newRequestBody) {
            this.newRequestBody = newRequestBody;
        }
    }

    public class ModifyResponseBodyWrapper extends HttpServletResponseWrapper {


        private ByteArrayOutputStream bos;

        public ModifyResponseBodyWrapper(HttpServletResponse response) {
            super(response);
            this.bos = new ByteArrayOutputStream();
        }

        @Override
        public ServletOutputStream getOutputStream() throws IOException {
            return new ServletOutputStream() {
                @Override
                public boolean isReady() {
                    return true;
                }

                @Override
                public void setWriteListener(WriteListener writeListener) {

                }

                @Override
                public void write(int b) throws IOException {
                    bos.write(b);
                }

                @Override
                public void write(byte[] b) throws IOException {
                    bos.write(b);
                }
            };
        }

        @Override
        public PrintWriter getWriter() throws IOException {
            return new PrintWriter(new OutputStreamWriter(bos));
        }

        public String getResponseBody() throws IOException {
            ServletOutputStream outputStream = this.getOutputStream();
            outputStream.flush();

            PrintWriter writer = this.getWriter();
            writer.flush();

            return bos.toString();
        }
    }
}
